#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#include "SoftI2CPlus.h"

#include <util/delay.h>
#include <string.h>

#define i2cbitdelay 4

// WIRE_HAS_END means Wire has end()
#define WIRE_HAS_END 1

#define I2C_ACK 1
#define I2C_NAK 0

#define I2C_WRITE 0
#define I2C_READ 1

#define i2c_scl_release() \
    *SCL_dirReg &= ~SCL_bitMask
#define i2c_sda_release() \
    *SDA_dirReg &= ~SDA_bitMask

// sets SCL low and drives output
#define i2c_scl_lo()              \
    *SCL_portReg &= ~SCL_bitMask; \
    *SCL_dirReg |= SCL_bitMask;

// sets SDA low and drives output
#define i2c_sda_lo()              \
    *SDA_portReg &= ~SDA_bitMask; \
    *SDA_dirReg |= SDA_bitMask;

// set SCL high and to input (releases pin) (i.e. change to input,turnon pullup)
#define i2c_scl_hi()                 \
    *SCL_dirReg &= ~SCL_bitMask;     \
    if (usePullups)                  \
    {                                \
        *SCL_portReg |= SCL_bitMask; \
    }

// set SDA high and to input (releases pin) (i.e. change to input,turnon pullup)
#define i2c_sda_hi()                 \
    *SDA_dirReg &= ~SDA_bitMask;     \
    if (usePullups)                  \
    {                                \
        *SDA_portReg |= SDA_bitMask; \
    }

// 构造函数
SoftI2CPlus::SoftI2CPlus()
{
    // do nothing, use setPins() later
}

// 构造函数
SoftI2CPlus::SoftI2CPlus(uint8_t sclPin, uint8_t sdaPin)
{
    setPins(sclPin, sdaPin, true);
    i2c_init();
}

// 构造函数
SoftI2CPlus::SoftI2CPlus(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups)
{
    setPins(sclPin, sdaPin, pullups);
    i2c_init();
}

// 设置引脚并初始化，这是公有函数，给实例初始化用的
void SoftI2CPlus::setPinsInit(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups)
{
    setPins(sclPin, sdaPin, pullups);
    i2c_init();
}

// 私有函数

// 设置Arduino的引脚
// Turn Arduino pin numbers into PORTx, DDRx, and PINx
void SoftI2CPlus::setPins(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups)
{
    uint8_t port;

    usePullups = pullups;

    SCL_pin = sclPin;
    SDA_pin = sdaPin;

    SCL_bitMask = digitalPinToBitMask(sclPin);
    SDA_bitMask = digitalPinToBitMask(sdaPin);

    port = digitalPinToPort(sclPin);
    SCL_portReg = portOutputRegister(port);
    SCL_dirReg = portModeRegister(port);

    port = digitalPinToPort(sdaPin);
    SDA_portReg = portOutputRegister(port);
    SDA_dirReg = portModeRegister(port);
}

// i2c初始化
// Inits bitbanging port, must be called before using the functions below
void SoftI2CPlus::i2c_init(void)
{
    //I2C_PORT &=~ (_BV( I2C_SDA ) | _BV( I2C_SCL ));
    //*_sclPortReg &=~ (_sdaBitMask | _sclBitMask);
    i2c_sda_hi();
    i2c_scl_hi();

    _delay_us(i2cbitdelay);
}

// i2c开始，设置开始条件
// SDA、SCL都设置成高电平，SDA先产生下降沿，SCL再产生下降沿
// Send a START Condition
void SoftI2CPlus::i2c_start(void)
{
    // set both to high at the same time
    //I2C_DDR &=~ (_BV( I2C_SDA ) | _BV( I2C_SCL ));
    //*_sclDirReg &=~ (_sdaBitMask | _sclBitMask);
    i2c_sda_hi();
    i2c_scl_hi();

    _delay_us(i2cbitdelay);

    i2c_sda_lo();
    _delay_us(i2cbitdelay);

    i2c_scl_lo();
    _delay_us(i2cbitdelay);
}

// i2c重新开始
void SoftI2CPlus::i2c_restart(void)
{
    // set both to high at the same time (releases drive on both lines)
    //I2C_DDR &=~ (_BV( I2C_SDA ) | _BV( I2C_SCL ));
    //*_sclDirReg &=~ (_sdaBitMask | _sclBitMask);
    i2c_sda_hi();
    i2c_scl_hi();

    i2c_scl_lo(); // force SCL low
    _delay_us(i2cbitdelay);

    i2c_sda_release(); // release SDA
    _delay_us(i2cbitdelay);

    i2c_scl_release(); // release SCL
    _delay_us(i2cbitdelay);

    i2c_sda_lo(); // force SDA low
    _delay_us(i2cbitdelay);
}

// i2c通讯结束
// Send a STOP Condition
void SoftI2CPlus::i2c_stop(void)
{
    i2c_scl_hi();
    _delay_us(i2cbitdelay);

    i2c_sda_hi();
    _delay_us(i2cbitdelay);
}

// i2c写一个位
void SoftI2CPlus::i2c_writebit(uint8_t data)
{
    if (data > 0)
    {
        i2c_sda_hi();
    }
    else
    {
        i2c_sda_lo();
    }

    i2c_scl_hi();
    _delay_us(i2cbitdelay);

    i2c_scl_lo();
    _delay_us(i2cbitdelay);

    if (data > 0)
    {
        i2c_sda_lo();
    }
    _delay_us(i2cbitdelay);
}

// i2c读一个位
uint8_t SoftI2CPlus::i2c_readbit(void)
{
    i2c_sda_hi();
    i2c_scl_hi();
    _delay_us(i2cbitdelay);

    uint8_t port = digitalPinToPort(SDA_pin);
    volatile uint8_t *pinReg = portInputRegister(port);
    uint8_t c = *pinReg; // I2C_PIN;

    i2c_scl_lo();
    _delay_us(i2cbitdelay);

    return (c & SDA_bitMask) ? 1 : 0;
}

// i2c写一个字节
// write a byte to the I2C slave device
uint8_t SoftI2CPlus::i2c_write(uint8_t data)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        // 128是0x80
        i2c_writebit(data & 128);
        data <<= 1;
    }

    return i2c_readbit();
}

// i2c读一个字节
// read a byte from the I2C slave device
uint8_t SoftI2CPlus::i2c_read(uint8_t ack)
{
    uint8_t res = 0;

    for (uint8_t i = 0; i < 8; i++)
    {
        res <<= 1;
        res |= i2c_readbit();
    }

    if (ack)
        i2c_writebit(0);
    else
        i2c_writebit(1);

    _delay_us(i2cbitdelay);

    return res;
}

// 公有函数

// wire开始
// 将SDA和SCL都调为高电平
void SoftI2CPlus::begin(void)
{
    // 设置缓冲区
    rxBufferIndex = 0;
    rxBufferLength = 0;
    // 指定是否在传输状态
    transmitting = false;
    // 错误类型
    error = 0;

    // i2c初始化
    i2c_init();
}

// wire结束
void SoftI2CPlus::end(void)
{
}

// 设置wire通讯频率
void SoftI2CPlus::setClock(uint32_t clock)
{
}

// wire开始传输
void SoftI2CPlus::beginTransmission(uint8_t address)
{
    if (transmitting)
    {
        i2c_restart();
        // 清除读标志位，告诉i2c这是写操作
        error = i2c_write((address << 1) | I2C_WRITE) ? 0 : 2;
    }
    else
    {
        i2c_start();
        // 清除读标志位，告诉i2c这是写操作
        error = i2c_write((address << 1) | I2C_WRITE) ? 0 : 2;
    }
    // 指明此时正在传输
    // indicate that we are transmitting
    transmitting = 1;
}

// wire开始传输
void SoftI2CPlus::beginTransmission(int address)
{
    beginTransmission((uint8_t)address);
}

// wire结束传输
uint8_t SoftI2CPlus::endTransmission(uint8_t sendStop)
{
    uint8_t transError = error;
    if (sendStop)
    {
        i2c_stop();
        transmitting = 0;
    }
    error = 0;
    return transError;
}

// wire结束传输
// 这提供了与endTransmission的原始定义和预期行为的向后兼容性。
// This provides backwards compatibility with the original
// definition, and expected behaviour, of endTransmission
uint8_t SoftI2CPlus::endTransmission(void)
{
    return endTransmission(true);
}

// wire写数据
// must be called in:
// slave tx event callback
// or after beginTransmission(address)
size_t SoftI2CPlus::write(uint8_t data)
{
    if (i2c_write(data))
    {
        return 1;
    }
    else
    {
        if (error == 0)
            error = 3;
        return 0;
    }
}

// wire写数据
// must be called in:
// slave tx event callback
// or after beginTransmission(address)
size_t SoftI2CPlus::write(const uint8_t *data, size_t quantity)
{
    size_t trans = 0;
    for (size_t i = 0; i < quantity; ++i)
    {
        trans += write(data[i]);
    }
    return trans;
}

// wire请求数据
// 向缓冲区中写入数据
uint8_t SoftI2CPlus::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop)
{
    uint8_t localerror = 0;
    if (isize > 0)
    {
        // send internal address; this mode allows sending a repeated start to access
        // some devices' internal registers. This function is executed by the hardware
        // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers)
        beginTransmission(address);
        // the maximum size of internal address is 3 bytes
        if (isize > 3)
        {
            isize = 3;
        }
        // write internal register address - most significant byte first
        while (isize-- > 0)
            write((uint8_t)(iaddress >> (isize * 8)));
        endTransmission(false);
    }
    // clamp to buffer length
    if (quantity > BUFFER_LENGTH)
    {
        quantity = BUFFER_LENGTH;
    }
    // 从i2c中写入读标志
    if (transmitting)
    {
        i2c_restart();
        localerror = !i2c_write((address << 1) | I2C_READ);
    }
    else
    {
        i2c_start();
        localerror = !i2c_write((address << 1) | I2C_READ);
    }

    if (error == 0 && localerror)
        error = 2;
    // 将数据写入到缓冲区
    // perform blocking read into buffer
    for (uint8_t cnt = 0; cnt < quantity; cnt++)
        rxBuffer[cnt] = i2c_read(cnt == quantity - 1);
    // set rx buffer iterator vars
    rxBufferIndex = 0;
    rxBufferLength = quantity;
    if (sendStop)
    {
        transmitting = 0;
        i2c_stop();
    }
    return quantity;
}

// wire请求数据
uint8_t SoftI2CPlus::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
{
    return requestFrom((uint8_t)address, (uint8_t)quantity, (uint32_t)0, (uint8_t)0, (uint8_t)sendStop);
}

// wire请求数据
uint8_t SoftI2CPlus::requestFrom(int address, int quantity, int sendStop)
{
    return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop);
}

// wire请求数据
uint8_t SoftI2CPlus::requestFrom(uint8_t address, uint8_t quantity)
{
    return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t) true);
}

// wire请求数据
uint8_t SoftI2CPlus::requestFrom(int address, int quantity)
{
    return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t) true);
}

// wire缓冲区是否有效
int SoftI2CPlus::available(void) {
    return rxBufferLength - rxBufferIndex;
}

// wire读数据
// 读完数据后删除
int SoftI2CPlus::read(void)
{
    int value = -1;
    if (rxBufferIndex < rxBufferLength)
    {
        value = rxBuffer[rxBufferIndex];
        ++ rxBufferIndex;
    }
    return value;
}

// wire读数据
// 读完数据后不删除
int SoftI2CPlus::peek(void)
{
    int value = -1;

    if(rxBufferIndex < rxBufferLength){
        value = rxBuffer[rxBufferIndex];
    }

    return value;
}

// wire清除
// Do nothing, use endTransmission(..) to force data transfer.
void SoftI2CPlus::flush(void)
{

}